#ifndef __EVENT_POLLER_H__
#define __EVENT_POLLER_H__

#include <unistd.h>
#include <sys/epoll.h>


#include "event.h"

class EventPoller
{
private:
    static constexpr size_t EVENT_MAX_COUNT = 1024;
    static constexpr int WAIT_TIMEOUT_MS = 10;   // epoll_wait 等待超时时间（单位为微秒）
public:
    EventPoller()
    {

    }

    int Init()
    {
        m_iEpollFd = epoll_create(1);
        if (m_iEpollFd == -1)
        {
            return -1;
        }

        return 0;
    }

    int AddEvent(IEventBase& refEvent, uint32_t uiEvents)
    {
        return __EventCtl(EPOLL_CTL_ADD, refEvent, uiEvents);
    }

    int ModEvent(IEventBase& refEvent, uint32_t uiEvents)
    {
        return __EventCtl(EPOLL_CTL_MOD, refEvent, uiEvents);
    }

    int DeleteEvent(IEventBase& refEvent)
    {
        struct epoll_event stEvent;
        return __EventCtl(EPOLL_CTL_DEL, refEvent, 0);
    }

    int Poll()
    {
        if (m_iEpollFd == -1)
        {
            return -1;
        }

        int iFdNum = epoll_wait(m_iEpollFd, m_astEvents, EVENT_MAX_COUNT, WAIT_TIMEOUT_MS);
        if (iFdNum == -1)
        {
            return -1;
        }

        struct epoll_event* pstEvent;

        for (int i = 0; i < iFdNum; i++)
        {
            IEventBase* pobjEvent = (IEventBase*)m_astEvents[i].data.ptr;
            if (pobjEvent == nullptr)
            {
                continue;
            }

            uint32_t uiEvents = pstEvent->events;
            if ((uiEvents | EPOLLIN) == EPOLLIN)
            {
                pobjEvent->HandleIn();
            }

            if ((uiEvents | EPOLLOUT) == EPOLLOUT)
            {
                pobjEvent->HandleOut();
            }

            if ((uiEvents | EPOLLHUP | EPOLLERR) != 0)
            {
                pobjEvent->HandleError();
                DeleteEvent(*pobjEvent);
            }
        }

        return 0;
    }

    ~EventPoller()
    {
        if (m_iEpollFd != -1)
        {
            close(m_iEpollFd);
            m_iEpollFd = -1;
        }
    }
private:
    int __EventCtl(int iOperType, IEventBase& refEvent, uint32_t uiEvents)
    {
        struct epoll_event stEvent;

        if (m_iEpollFd == -1)
        {
            return -1;
        }

        int iEventFd = refEvent.GetEventFd();
        if (iEventFd == -1)
        {
            return -1;
        }

        stEvent.data.ptr = (void*)&refEvent;
        stEvent.events = uiEvents;
        return epoll_ctl(m_iEpollFd, iOperType, iEventFd, &stEvent);
    }
private:
    int m_iEpollFd = -1;
    struct epoll_event m_astEvents[EVENT_MAX_COUNT];
};

#endif //__EVENT_POLLER_H__